package cn.ibizlab.odoo.util.valuerule.condition;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import cn.ibizlab.odoo.util.valuerule.VRSingleCondition;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import lombok.extern.slf4j.Slf4j;
import cn.ibizlab.odoo.util.log.IBIZLog;

/**
 * 常规条件
 * 常规条件(Simple)
 *
 * @param <T> 当前成员变量类型
 */
@Slf4j
@Data
@IBIZLog
public class VRSimpleCondition<T> extends VRSingleCondition<T> {

    //操作符号
    private OpType op;
    //参数类型
    private String paramType;
    //条件值
    private String paramValue;

    public VRSimpleCondition(String name, Boolean isNotMode, String ruleInfo, T value) {
        super(name, isNotMode, ruleInfo, value);

    }

    public VRSingleCondition<T> init(OpType op, String paramType, String paramValue) {
        this.op = op;
        this.paramType = paramType;
        this.paramValue = paramValue;
        return this;
    }

    @Override
    public boolean validate() {
        boolean isValid = true;

        if(value==null){
            throw new RuntimeException("校验值不能为null!");
        }

        if (op == OpType.EQ || op == OpType.NOTEQ
                || op == OpType.GT || op == OpType.GTANDEQ
                || op == OpType.LT || op == OpType.LTANDEQ) {
            //值比较逻辑
            isValid = compare(op);

        } else if (op == OpType.ISNULL || op == OpType.ISNOTNULL || op == OpType.TESTNULL) {
            //null、空值比较逻辑
            isValid = nullValueValid(op);

        } else if (op == OpType.LIKE || op == OpType.LEFTLIKE || op == OpType.RIGHTLIKE || op == OpType.USERLIKE) {
            //文本包含相关逻辑
            isValid = strlikeValid(op);

        } else if (op == OpType.IN || op == OpType.NOTIN) {
            //值范围相关逻辑
            isValid = rangeValid(op);

        } else if (op == OpType.BITAND) {
            //位与操作
            isValid = bitAndValid();
        }

        return isValid;
    }

    /**
     * 比较逻辑，操作标识为： EQ, NOTEQ, GT, GTANDEQ, LT, LTANDEQ 的情况。
     *
     * @param op
     * @return
     */
    private boolean compare(OpType op) {

        if (!(value instanceof Comparable)) {
            //当前字段为不可比较类型。
            throw new RuntimeException("配置错误，不支持当前类型比较：" + value.getClass());
        }

        Comparable valueCompare = (Comparable) value;

        if ("CURTIME".equals(paramType)) {
            //当前时间戳比较
            if( value.getClass() != Timestamp.class ){
            throw new RuntimeException("配置错误，不支持当前类型比较：" + value.getClass());
            }
            return compareToOP(op, valueCompare, new Timestamp(new Date().getTime()));

        } else if ("ENTITYFIELD".equals(paramType)) {
            //数据对象属性比较
            //TODO  待拓展。
            return true;

        } else if (StringUtils.isEmpty(paramType)) {
            //直接值比较。
            return compareToOP(op, valueCompare, paramValue);
        }

        return false;
    }

    /**
     * a.compareTo(b) 结果转换为boolen值。
     *
     * @param op    EQ, NOTEQ, GT, GTANDEQ, LT, LTANDEQ
     * @param value 属性值
     * @param param 比较值。
     * @return true/false
     */
    private boolean compareToOP(OpType op, Comparable value, Object param) {
        Object parsedParam = param;

        //直接值比较中，处理类型转换。
        if (param instanceof String) {
            if (value instanceof Integer) {
                parsedParam = Integer.valueOf(String.valueOf(param));

            } else if (value instanceof Double) {
                parsedParam = Double.valueOf(String.valueOf(param));

            } else if (value instanceof Date) {
                parsedParam = Timestamp.valueOf(String.valueOf(param));
            }
        }

        switch (op) {
            case EQ:
                return value.compareTo(parsedParam) == 0;
            case NOTEQ:
                return value.compareTo(parsedParam) != 0;
            case GT:
                return value.compareTo(parsedParam) > 0;
            case GTANDEQ:
                return value.compareTo(parsedParam) >= 0;
            case LT:
                return value.compareTo(parsedParam) < 0;
            case LTANDEQ:
                return value.compareTo(parsedParam) <= 0;
        }
        return false;
    }

    private boolean nullValueValid(OpType op) {
        switch (op) {
            case ISNULL:
                return value == null;
            case ISNOTNULL:
                return !(value == null);
            case TESTNULL:
                return (StringUtils.isEmpty(value) || "".equals(String.valueOf(value).trim()));
        }
        return false;
    }


    private boolean strlikeValid(OpType op) {
        String valueStr = String.valueOf(value);
        String paramStr = String.valueOf(paramValue);
        switch (op) {
            case LIKE:
                return valueStr.contains(paramStr);
            case LEFTLIKE:
                return valueStr.contains(paramStr) && valueStr.startsWith(paramStr);
            case RIGHTLIKE:
                return valueStr.contains(paramStr) && valueStr.endsWith(paramStr);
            case USERLIKE:
                return userLike(valueStr);
        }
        return true;
    }

    /**
     * 自定义匹配，eg: *项目*组。
     *
     * @param valuestr
     * @return
     */
    private boolean userLike(final String valuestr) {
        String userLikePattern = String.valueOf(paramValue).replace("*", "\\w+");
        return valuestr.matches(userLikePattern);
    }

    /**
     * 值范围检查
     * 数值范围检查： paramValue a;b
     * 其他(字符串)范围检查：paramValue a;b;c;d;....;n
     *
     * @param op IN,OUT
     * @return true/false
     */
    private boolean rangeValid(OpType op) {
        boolean inOrOut = op == OpType.IN;
        if (value instanceof Number) {
            Double doubleValue = Double.valueOf(String.valueOf(value == null ? "" : value));
            return numInrange(inOrOut);
        } else {
            return strInrange(inOrOut);
        }
    }

    /**
     * 数值值范围判断。
     * paramValue:格式为，a;b -->值在a~b之间
     *
     * @param inOrOut true:范围中，false：范围外
     * @return true/false
     */
    private boolean numInrange(boolean inOrOut) {
        if (this.value instanceof Number) {
            Double valueD = Double.valueOf(String.valueOf(value == null ? "" : value));
            String[] values = String.valueOf(paramValue == null ? "" : paramValue).split(";");
            if (values.length != 2) {
                throw new RuntimeException("数据范围参数错误！必须为以下格式：【a;b】");
            }
            Double min = Double.valueOf(values[0]);
            Double max = Double.valueOf(values[2]);
            boolean inrange = valueD > min && valueD < max;
            return inOrOut ? inrange : (!inrange);
        }
        return false;
    }

    /**
     * 字符串值范围判断。
     * paramValue:格式为，a;b;c....;n -->值在a,b,c,....n之前(枚举）
     *
     * @param inOrOut true:范围中，false：范围外
     * @return true/false
     */
    private boolean strInrange(boolean inOrOut) {
        String[] values = String.valueOf(paramValue == null ? "" : paramValue).split(";");
        List<String> valueList = Arrays.asList(values);
        boolean inrange = valueList.contains(value);
        return inOrOut ? inrange : (!inrange);
    }

    /**
     * 位与操作。
     * 只支持整形，否则会报异常。
     * paramValue格式：【操作值；校验值】
     * 校验逻辑：
     * 成员变量值 & 操作值==校验值
     *
     * @return
     */
    private boolean bitAndValid() {
        if (!(value instanceof Integer)) {
            throw new RuntimeException("位与校验仅支持整形类型。");
        }
        Integer valueInt = Integer.valueOf(String.valueOf(value));
        String[] values = String.valueOf(paramValue == null ? "" : paramValue).split(";");
        try {
            Integer opValue = Integer.valueOf(values[0]);
            Integer checkValue = Integer.valueOf(values[1]);
            return (valueInt & opValue) == checkValue;
        } catch (Exception e) {
            throw new RuntimeException("配置的值规则参数错误，正确格式为：【操作值；校验值】");
        }
    }

    //操作类型枚举。
    public enum OpType {
        EQ, NOTEQ, GT, GTANDEQ, LT, LTANDEQ,
        ISNULL, ISNOTNULL, TESTNULL,
        LIKE, LEFTLIKE, RIGHTLIKE, USERLIKE,
        IN, NOTIN,
        BITAND
    }

}
